<?php declare(strict_types=1);

namespace application\units;

/**
 * Class Di
 * @package application\units
 *
 * @property \application\units\Db                  $db
 * @property \application\units\Request             $request
 * @property \application\units\RpcClient           $rpcClient
 * @property \application\units\Auth                $auth
 * @property \application\units\BusinessCommonQuery $businessCommonQuery
 */
class Di implements \ArrayAccess
{
    
    /**
     * 注册的服务
     */
    protected $_services;
    
    /**
     * @var Di
     */
    protected static $_instance;
    
    /**
     * 单例的服务对象
     */
    protected $_instances = [];
    
    private function __construct()
    {
    }
    
    private function __clone()
    {
    }
    
    public static function getInstance()
    {
        return static::$_instance = static::$_instance ? static::$_instance : new static();
    }
    
    public function make($name)
    {
        if (!isset($this->_services[$name])) {
            throw new \Exception($name . ' Di service not found');
        }
        $service = $this->_services[$name]->bindTo($this);
        return $service();
    }
    
    public function singleton($name)
    {
        if (!isset($this->_instances[$name])) {
            $this->_instances[$name] = $this->make($name);
        }
        return $this->_instances[$name];
    }
    
    public function register($name, $service)
    {
        if (is_object($service) && !is_callable($service)) {
            $this->_instances[$name] = $service;
        } else {
            $this->set($name, $service);
        }
        
        return $this;
    }
    
    public function get($name)
    {
        return $this->singleton($name);
    }
    
    public function set($name, $definition)
    {
        if ($definition instanceof \Closure) {
            $this->_services[$name] = $definition;
        } else {
            throw new \Exception('Di service must instanceof Closure');
        }
    }
    
    public function _unset($name)
    {
        unset($this->_services[$name]);
    }
    
    public function _isset($name)
    {
        return isset($this->_services[$name]);
    }
    
    public function offsetExists($offset)
    {
        return $this->_isset($offset);
    }
    
    public function offsetGet($offset)
    {
        return $this->singleton($offset);
    }
    
    public function offsetSet($offset, $value)
    {
        if ($value instanceof \Closure) {
            $this->set($offset, $value);
        } else {
            throw new \Exception('Di service must instanceof Closure');
        }
    }
    
    public function offsetUnset($offset)
    {
        $this->_unset($offset);
    }
    
    public function __isset($name)
    {
        return $this->_isset($name);
    }
    
    public function __get($name)
    {
        return $this->singleton($name);
    }
    
    public function __set($name, $value)
    {
        $this->set($name, $value);
    }
    
}